package com.sean.common.mybatis;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.parameter.DefaultParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import com.sean.common.mybatis.dialect.DB2Dialect;
import com.sean.common.mybatis.dialect.Dialect;
import com.sean.common.mybatis.dialect.MySQLDialect;
import com.sean.common.mybatis.dialect.OracleDialect;
import com.sean.common.page.PageRowBounds;
import com.sean.common.utils.StringUtils;


/**
 * 分页查询拦截器.
 * 
 * @author XinWang
 * 
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
	// 日志对象
	protected static final Log log = LogFactory.getLog(PaginationInterceptor.class);
	
	private String dialect = "DB2";
	
	private int pagesize = 10;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin
	 * .Invocation)
	 */
	public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException,
			SQLException {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);
		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
		PageRowBounds pageRowBounds = null;
		if (rowBounds instanceof PageRowBounds) {
			pageRowBounds = (PageRowBounds) rowBounds;
		} else {
			return invocation.proceed();
		}
		DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler
				.getValue("delegate.parameterHandler");
		// 获取查询用SQL
		String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
		// 设置分页参数
		Dialect dialect = this.getDialect();
		// 获取总条数
		int rowCount = this.getCount(defaultParameterHandler, configuration, dialect, originalSql);
		if(pageRowBounds.getPageSize() == 0) {
			pageRowBounds.setPageSize(pagesize);// 设置每页显示条数
		}
		pageRowBounds.setCount(rowCount);
		// 设置排序参数
		@SuppressWarnings("unchecked")
		Map<String, Object> parameterMap = (Map<String, Object>) defaultParameterHandler.getParameterObject();
		String sort = (String) parameterMap.get("_sort");
		String order = (String) parameterMap.get("_order");
		if (StringUtils.isNotBlank(sort) && StringUtils.isNotBlank(order)) {
			originalSql = originalSql + " order by " + sort + " " + order;
		}
		// 设置分页参数
		metaStatementHandler.setValue(
				"delegate.boundSql.sql",
				dialect.getLimitString(originalSql, pageRowBounds.getOffsetForPagination(),
						pageRowBounds.getLimitForPagination()));
		metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
		if (log.isDebugEnabled()) {
			BoundSql boundSql = statementHandler.getBoundSql();
			log.debug("生成分页SQL : " + boundSql.getSql());
		}
		return invocation.proceed();
	}

	private Dialect getDialect() {
		Dialect.Type databaseType = null;
		try {
			databaseType = Dialect.Type.valueOf(dialect.toUpperCase());
		} catch (Exception e) {
			// ignore
		}
		if (databaseType == null) {
			throw new RuntimeException("数据库方言设置失败!当前设置为:" + dialect);
		}
		Dialect dialect = null;
		switch (databaseType) {
		case ORACLE:
			dialect = new OracleDialect();
			break;
		case DB2:// 需要实现DB2SQL的分页逻辑
			dialect = new DB2Dialect();
			break;
		case MYSQL:// 需要实现MySQL的分页逻辑
			dialect = new MySQLDialect();
			break;
		}
		return dialect;
	}

	private int getCount(DefaultParameterHandler defaultParameterHandler, Configuration configuration, Dialect dialect,
			String originalSql) throws SQLException {
		Connection connection = null;
		PreparedStatement countStmt = null;
		ResultSet result = null;
		int rowCount;
		try {
			connection = configuration.getEnvironment().getDataSource().getConnection();
			rowCount = 0;
			String countsql = dialect.getCountString(originalSql);
			if (log.isDebugEnabled()) {
				log.debug("获取记录总数SQL : " + countsql);
			}
			countStmt = connection.prepareStatement(countsql);
			defaultParameterHandler.setParameters(countStmt);
			result = countStmt.executeQuery();
			if (result.next()) {
				rowCount = result.getInt(1);
			}
		} catch (Exception e) {
			log.error("分页查询失败:未能获取记录总数!", e);
			throw new SQLException("分页查询失败:未能获取记录总数!");
		} finally {
			if (result != null)
				result.close();
			if (countStmt != null)
				countStmt.close();
			if (connection != null)
				connection.close();
		}
		return rowCount;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setDialect(String dialect) {
		this.dialect = dialect;
	}

	public void setPagesize(int pagesize) {
		this.pagesize = pagesize;
	}

	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		
	}
}